%{
/*
 *
 * sub-Pascal grammar in Yacc format
 */
#include <stdio.h>
#include "treestruct.hpp"

tree* lexer_parser(char* input_file);
tree* result;
int yyerror(char *s);

//extern "C"{
    int yyparse(void);
    int yylex(void);
    int yywrap()
    {
        return 1;
    }
//}
%}

%union {
	int a_number;
	int a_boolean;
	char* a_variable;
	tree* a_tree;
	enum ast_ops an_op;
}



%token PROGRAM DOT SEMICOLON COMMA IDENTIFIER CONST CONST_BOOL CONST_INT CHARACTER_STRING EQUAL PLUS MINUS TYPE
%token ARRAY LBRAC RBRAC OF DOTDOT RECORD COLON PBEGIN END VAR PRIMITIVE_TYPE
%token LT GT LE NOTEQUAL GE OR AND DIV MOD STAR LPAREN RPAREN NOT ASSIGNMENT
%token IF THEN WHILE DO PROCEDURE FUNCTION

%type <an_op> MultiplyingOperator AddingOperator SignOperator RelationalOperator

%type <a_number> CONST_INT Numeral

%type <a_boolean> CONST_BOOL

%type <a_variable> PRIMITIVE_TYPE IDENTIFIER

%type <a_tree> program BlockBody ProcedureAndFunctionDefinition
%type <a_tree> constant ConstantDefinition ConstantDefinitionList ConstantDefinitionPart
%type <a_tree> identifier identifierList
%type <a_tree> TypeNameUse TypeDefinitionPart TypeDefinitionList TypeDefinition
%type <a_tree> VariableDefinitionPart VariableDefinitionList VariableDefinition VariableAccess
%type <a_tree> SimpleExpression ComplexExpression Expression Factor Term
%type <a_tree> StatementList Statement Empty AssignmentStatement ProcedureStatement IfStatement WhileStatement CompoundStatement
%type <a_tree> FunctionDesignator ActualParameter ActualParametersL ActualParameterList
%type <a_tree> NewType NewArrayType IndexRange NewRecordType FieldList RecordSectionList RecordSection
%type <a_tree> ParameterNameDefList ParameterDefinition ParameterDefinitions FormalParameterList
%type <a_tree> ProcedureBlock ProcedureDefinition FunctionBlock FunctionDefinition

%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE


%%


program : PROGRAM identifier semicolon BlockBody DOT { result = make_operateur($2, $4, NULL, PROGRAM_OP); $$ = result; printf("SYNTAX OK\n"); }
;



BlockBody : ConstantDefinitionPart
	TypeDefinitionPart
	VariableDefinitionPart
	ProcedureAndFunctionDefinition
	CompoundStatement { $$ = make_operateur(make_operateur($1, $2, $3, DATADEF_OP), $4, $5, BODY_OP); }
 ;



ConstantDefinitionPart : CONST ConstantDefinitionList { $$ = $2; }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

ConstantDefinitionList :ConstantDefinitionList ConstantDefinition { $$ = make_operateur($2, $1, NULL, CONSTLIST_OP); }
 | ConstantDefinition { $$ = $1; }
 ;

ConstantDefinition : identifier  EQUAL constant SEMICOLON { $$ = make_operateur($1, $3, NULL, CONSTDEF_OP); }
 ;

constant : CONST_BOOL { $$ = make_boolean($1); }
 | Numeral { $$ = make_number($1); }
 | identifier { $$ = $1; }
 ;

/* Numeral is just a integer number - in other case there is shift/reduce conflict in SimpleExpression->
 *  SignOperator->Term->Factor->Number  so grammar in specification would be incorrect
*/

Numeral : CONST_INT { $$ = $1; }
/* | sign CONST_INT*/
 ;

/*
sign : PLUS
 | MINUS
 ;
*/


TypeDefinitionPart : TYPE TypeDefinitionList { $$ = $2; }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

TypeDefinitionList : TypeDefinitionList TypeDefinition { $$ = make_operateur($2, $1, NULL, TYPEDEFLIST_OP); }
 | TypeDefinition { $$ = $1; }
 ;

TypeDefinition : identifier EQUAL NewType semicolon { $$ = make_operateur($1, $3, NULL, TYPEDEF_OP); }
 ;

NewType : NewArrayType { $$ = $1; }
 | NewRecordType { $$ = $1; }
 ;

NewArrayType : ARRAY LBRAC IndexRange RBRAC OF TypeNameUse { $$ = make_operateur($3, $6, NULL, ARRAYDEF_OP); }
 ;

IndexRange : constant DOTDOT constant { $$ = make_operateur($1, $3, NULL, RANGE_OP); }
 ;

TypeNameUse : PRIMITIVE_TYPE { $$ = make_variable($1); }
 | identifier { $$ = $1; }
 ;


NewRecordType : RECORD FieldList END { $$ = make_operateur($2, NULL, NULL, RECORDDEF_OP); }
 ;

FieldList : RecordSectionList { $$ = $1; }
 ;

RecordSectionList : RecordSectionList SEMICOLON RecordSection { $$ = make_operateur($3, $1, NULL, RECORDLIST_OP); }
 | RecordSection { $$ = $1; }
 ;



RecordSection : identifierList COLON TypeNameUse { $$ = make_operateur($3, $1, NULL, RECORDIDLIST_OP); }
 ;

identifierList : identifierList comma identifier { $$ = make_operateur($3, $1, NULL, IDENTIFIERLIST_OP); }
 | identifier { $$ = $1; }
 ;




VariableDefinitionPart : VAR VariableDefinitionList { $$ = $2; }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

VariableDefinitionList : VariableDefinitionList VariableDefinition { $$ = make_operateur($2, $1, NULL, VARLIST_OP); }
 | VariableDefinition { $$ = $1; }
 ;

VariableDefinition : identifierList COLON TypeNameUse SEMICOLON { $$ = make_operateur($3, $1, NULL, VARTYPE_OP); }
 ;





Expression : SimpleExpression ComplexExpression { if ((*$2).an_operateur.first == NULL) { $$ = $1; } else
	{ $$ = make_operateur($1, (*$2).an_operateur.first, NULL, (*$2).an_operateur.operateur); } }
 ;

ComplexExpression : RelationalOperator SimpleExpression { $$ = make_operateur($2, NULL, NULL, $1); }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

RelationalOperator : LT { $$ = LT_OP; }
 | EQUAL { $$ = EQUAL_OP; }
 | GT { $$ = GT_OP; }
 | LE { $$ = LE_OP; }
 | NOTEQUAL { $$ = NOTEQUAL_OP; }
 | GE { $$ = GE_OP; }
 ;

SimpleExpression :  SignOperator Term { if ($1 == PLUS_OP) { $$ = $2; } else { $$ = make_operateur(make_number(0), $2, NULL, $1); } }
 | SimpleExpression AddingOperator Term { $$ = make_operateur($3, $1, NULL, $2); }
 ;


SignOperator : PLUS { $$ = PLUS_OP; }
 | MINUS { $$ = MINUS_OP; }
 | { $$ = PLUS_OP; }
 ;

AddingOperator : PLUS { $$ = PLUS_OP; }
 | MINUS { $$ = MINUS_OP; }
 | OR { $$ = OR_OP; }
 ;

Term : Factor { $$ = $1; }
 | Term MultiplyingOperator Factor { $$ = make_operateur($3, $1, NULL, $2); }
 ;

MultiplyingOperator : STAR { $$ = STAR_OP; }
 | DIV { $$ = DIV_OP; }
 | MOD { $$ = MOD_OP; }
 | AND { $$ = AND_OP; }
 ;

Factor : Numeral { $$ = make_number($1); }
 | VariableAccess { $$ = $1; }
 | FunctionDesignator { $$ = $1; }
 | LPAREN Expression RPAREN { $$ = $2; }
 | NotOperator Factor { $$ = make_operateur($2, NULL, NULL, NOT_OP); }
 ;

NotOperator : NOT
 ;

VariableAccess : identifier { $$ = $1; }
 | VariableAccess LBRAC Expression RBRAC { $$ = make_operateur($1, $3, NULL, ARRAYACCES_OP); }
 | VariableAccess DOT identifier { $$ = make_operateur($1, $3, NULL, RECORDACCES_OP); }
 ;

FunctionDesignator : identifier ActualParameterList { $$ = make_operateur($1, $2, NULL, FUNCCALL_OP); }
 ;

Statement : AssignmentStatement { $$ = $1; }
 | ProcedureStatement { $$ = $1; }
 | IfStatement { $$ = $1; }
 | WhileStatement { $$ = $1; }
 | CompoundStatement { $$ = $1; }
 | Empty { $$ = $1; }
 ;

Empty : { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;


AssignmentStatement : VariableAccess ASSIGNMENT Expression { $$ = make_operateur($1, $3, NULL, ASSIGNMENT_OP); }
 ;

ProcedureStatement : identifier ActualParameterList { $$ = make_operateur($1, $2, NULL, PROCCALL_OP); }
 | identifier { $$ = $1; }
 ;

ActualParameterList : LPAREN ActualParametersL RPAREN { $$ = $2; }
 ;

ActualParametersL : ActualParametersL COMMA ActualParameter { $$ = make_operateur($3, $1, NULL, ARGLIST_OP); }
 | ActualParameter { $$ = make_operateur($1, NULL, NULL, ARG_OP); }
 ;

ActualParameter : Expression { $$ = $1; }
 ;

/*
IfStatement : IF Expression THEN Statement
 |IF Expression THEN Statement ELSE Statement
 ;
*/


IfStatement : IF Expression THEN Statement %prec LOWER_THAN_ELSE { $$ = make_operateur($2, $4, NULL, IFTHEN_OP); }
 | IF Expression THEN Statement ELSE Statement { $$ = make_operateur($2, $4, $6, IFTHENELSE_OP); }
 ;



WhileStatement : WHILE Expression DO Statement { $$ = make_operateur($2, $4, NULL, WHILE_OP); }
 ;



CompoundStatement:  PBEGIN StatementList END { $$ = make_operateur($2, NULL, NULL, COMPOUND_OP); }
 ;

StatementList : StatementList SEMICOLON Statement { $$ = make_operateur($3, $1, NULL, STATEMENTLIST_OP); }
 | Statement { $$ = make_operateur($1, NULL, NULL, STATEMENT_OP); }
 ;

/*================== ProcedureAndFunctionDefinition added to specification =====================*/
ProcedureAndFunctionDefinition : ProcedureAndFunctionDefinition FunctionDefinition { $$ = make_operateur($2, $1, make_variable("function"), PROCFUNCLIST_OP); }
 | ProcedureAndFunctionDefinition ProcedureDefinition { $$ = make_operateur($2, $1, make_variable("procedure"), PROCFUNCLIST_OP); }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

/*==============================================================================================*/


/* ================= FUNCTION added to specification =======================*/

FunctionDefinition : FUNCTION identifier FunctionBlock SEMICOLON { $$ = make_operateur($2, $3, NULL, FUNCDEF_OP); }
 ;

FunctionBlock : FormalParameterList COLON TypeNameUse SEMICOLON BlockBody { $$ = make_operateur($3, $1, $5, FUNCBLOCK_OP); }
 ;

/* ======================================================================== */


ProcedureDefinition : PROCEDURE identifier ProcedureBlock SEMICOLON { $$ = make_operateur($2, $3, NULL, PROCDEF_OP); }
 ;


ProcedureBlock : FormalParameterList SEMICOLON BlockBody { $$ = make_operateur($1, $3, NULL, PROCBLOCK_OP); }
 ;

FormalParameterList : LPAREN ParameterDefinitions RPAREN { $$ = $2; }
 | { $$ = make_operateur(NULL, NULL, NULL, Empty_OP); }
 ;

ParameterDefinitions : ParameterDefinitions SEMICOLON ParameterDefinition { $$ = make_operateur($3, $1, NULL, PARADEFLIST_OP); }
 | ParameterDefinition { $$ = $1; }
 ;

ParameterDefinition : VAR ParameterNameDefList COLON TypeNameUse { $$ = make_operateur($4, $2, NULL, PARATYPE_VAR_OP); }
 | ParameterNameDefList COLON TypeNameUse { $$ = make_operateur($3, $1, NULL, PARATYPE_NOVAR_OP); }
 ;

ParameterNameDefList : identifier { $$ = $1; }
 | ParameterNameDefList COMMA identifier { $$ = make_operateur($3, $1, NULL, PARALIST_OP); }
 ;





identifier : IDENTIFIER { $$ = make_variable($1); }
 ;

semicolon : SEMICOLON
 ;

comma : COMMA
 ;

%%

//#include <stdio.h>
//#include "lex.yy.c"

using namespace std;
#include <iostream>
extern FILE *yyin;
int yydebug=1;

tree* lexer_parser(char* input_file)
{
    cout << " in here" << endl;
    yyin = fopen(input_file, "r");
    cout << " out here" << endl;

    //yyout = fopen("ast_output.temp", "w");

    yyparse();
      cout << "finished yyparse" << endl;

    //fprinttree(result, 1);
    fclose(yyin);
    //fclose(yyout);
	return (result);
}

int yyerror(char *s)
{
   printf("ERROR: %s\n", s);
   return 1;
}


